package com.kaithan.gateway.filter;

import com.kaithan.framework.common.exception.CustomAssert;
import com.kaithan.gateway.util.CaffeineUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import reactor.core.publisher.Flux;

import java.nio.charset.StandardCharsets;

/**
 * 类描述：网关服务全局请求日志过滤器
 *
 * @author ZhuYin
 * @since 2025年02月14日
 */
@Slf4j
@Configuration
public class GlobalLoggingFilter {

    /**
     * 方法说明：打印请求信息
     *
     * @return {@link GlobalFilter}
     * @author ZhuYin
     * @since 2025年02月14日
     */
    @Bean
    public GlobalFilter logRequestFilter() {
        return (exchange, chain) -> {
            // 获取原始请求
            ServerHttpRequest originalRequest = exchange.getRequest();

            // 创建线程安全的 StringBuilder，用于拼接日志内容
            StringBuilder sb = new StringBuilder();
            sb.append("\n||============================ Request Start ============================");
            sb.append("\n||     Request Method: ").append(originalRequest.getMethod());
            sb.append("\n||        Request URI: ").append(originalRequest.getURI());
            sb.append("\n||       Query Params: ").append(originalRequest.getQueryParams());
            sb.append("\n||    Request Headers: ").append(originalRequest.getHeaders());

            HttpHeaders headers = originalRequest.getHeaders();
            // 前端每个请求的请求头上必须携带的UUID参数
            String requestId = headers.getFirst("X-Request-Id");
            CustomAssert.notNull(requestId, "Request Header [X-Request-Id] must not be null");
            // 创建一个装饰器，用于缓存请求体并打印日志
            ServerHttpRequest decoratedRequest = new ServerHttpRequestDecorator(originalRequest) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return super.getBody()
                            .map(dataBuffer -> {
                                // 缓存数据并记录日志（仅适用于非 GET 请求）
                                byte[] content = null;
                                if (!HttpMethod.GET.equals(originalRequest.getMethod())) {
                                    content = new byte[dataBuffer.readableByteCount()];
                                    dataBuffer.read(content);
                                    DataBufferUtils.release(dataBuffer); // 释放原始缓冲区
                                    String body = new String(content, StandardCharsets.UTF_8);
                                    sb.append("\n||       Request Body: ").append(body);
                                }
                                // 从 exchange 中获取 DataBufferFactory
                                DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
                                return bufferFactory.wrap(content);
                            });
                }
            };

            // 使用装饰后的请求继续处理链
            return chain.filter(exchange.mutate().request(decoratedRequest).build())
                    .doOnSuccess(v -> {
                        // 确保每个请求的日志只被记录一次
                        if (!CaffeineUtil.containsKey(requestId)) {
                            sb.append("\n||============================ Request End ==============================\n");
                            log.info(sb.toString());
                            // 标记该请求已记录日志
                            CaffeineUtil.set(requestId, true, 2000);
                        }
                    })
                    .doOnError(e -> {
                        // 确保每个请求的错误日志只被记录一次
                        if (!CaffeineUtil.containsKey(requestId)) {
                            sb.append("\n||                   Error: ").append(e.getMessage());
                            sb.append("\n||============================ Request End ==============================\n");
                            log.error(sb.toString(), e);
                            // 标记该请求已记录日志
                            CaffeineUtil.set(requestId, true, 2000);
                        }
                    });
        };
    }
}